import { defineAsyncComponent, onUnmounted, createApp, computed, ref, h } from 'vue';
import zhCN from 'ant-design-vue/lib/locale/zh_CN.js';
import Modal from 'ant-design-vue/lib/modal/index.js';
import ConfigProvider from 'ant-design-vue/lib/config-provider/index.js';

function defineProperty() {
  try {
    Object.defineProperty.apply(Object, arguments);
  } catch (e) {
    return e;
  }
}

function tap() {
  let args, res, afterHandle;
  args = [].slice.call(arguments, 0);
  if (typeof args[args.length - 1] === 'function') {
    [afterHandle] = args.splice(args.length - 1, 1);
  } else {
    afterHandle = () => {};
  }
  if (typeof args[0] === 'function') {
    res = args[0](...args.slice(1));
  } else {
    const obj = args[0];
    const key = args[1];
    if (obj && typeof obj === 'object' && typeof key === 'string' && typeof obj[key] === 'function') {
      res = obj[key](...args.slice(2));
    }
  }
  if (res instanceof Promise) {
    res.then((e) => e !== false && afterHandle(e));
  } else {
    res !== false && afterHandle(res);
  }
}

const QueryHeight = (function () {
  let ins = null;
  return class {
    constructor() {
      if (ins) {
        return ins;
      }
      let getMaxHeight = () => window.innerHeight - 300;
      let onResize = () => {
        this.data.height = getMaxHeight();
        this.style.height = getMaxHeight() + 'px';
        // console.log(this.data, this.style);
        if (typeof this.onResize === 'function') {
          this.onResize({ data: this.data, style: this.style });
        }
      };
      this.data = { height: getMaxHeight() };
      this.style = { height: getMaxHeight() + 'px' };
      this.dispose = () => {
        this.onResize = null;
      };
      this.destroy = () => {
        window.removeEventListener('resize', onResize, false);
        getMaxHeight = null;
        onResize = null;
        this.onResize = null;
        this.destroy = null;
        this.style = null;
        this.data = null;
        ins = null;
      };
      window.addEventListener('resize', onResize, false);
      ins = this;
    }
  };
})();

export const dialogSize = { small: 600, medium: 900, large: 1200, max: 1500 };

export function dialog({ content, contentProps, dialogProps }) {
  return new Promise((resolve) => {
    let styleSheet, dialogDiv, queryHeight, vNode, app, timer;
    contentProps = Object.assign({}, contentProps);
    dialogProps = Object.assign({}, dialogProps);
    queryHeight = new QueryHeight();
    dialogDiv = document.createDocumentFragment();
    styleSheet = document.createElement('style');
    styleSheet.appendChild(document.createTextNode('body { overflow: hidden !important; }'));
    document.head.appendChild(styleSheet);
    vNode = h(
      {
        emits: ['mounted'],
        props: {
          content: { required: true, default: undefined },
          contentProps: { type: Object, default: () => ({}) },
          dialogProps: { type: Object, default: () => ({}) },
        },
        setup(props, ctx) {
          let content = props.content;
          let contentProps = props.contentProps;
          let dialogProps = props.dialogProps;
          let modalRef = ref(null);
          let componentRef = ref(null);
          let visible = ref(true);
          let bodyStyle = ref({});
          queryHeight.onResize = ({ style }) => {
            const fixed = dialogProps.height === 'fixed';
            bodyStyle.value = Object.assign(
              {
                [fixed ? 'height' : 'maxHeight']: style.height,
                overflow: 'hidden auto',
              },
              dialogProps.bodyStyle
            );
          };
          queryHeight.onResize(queryHeight);

          let getContainer = () => {
            if (typeof dialogProps.getContainer === 'function') {
              return dialogProps.getContainer();
            }
            return document.getElementById('app') || document.body;
          };

          let close = () => {
            return new Promise((resolve) => {
              visible.value = false;
              setTimeout(() => {
                app.unmount();
                resolve();
              }, 300);
            });
          };
          let onCancel = () => {
            tap(componentRef.value, 'onCancel', (res) => {
              tap(dialogProps.onCancel, res, close);
            });
          };
          let onOk = () => {
            tap(componentRef.value, 'onOk', (res) => {
              tap(dialogProps.onOk, res, close);
            });
          };
          let getSlot = (slot, props) => {
            if (typeof slot !== 'object' || !slot) {
              return;
            }
            /* component object */
            if (typeof slot.render === 'function') {
              try {
                slot = h(slot);
              } catch (e) {
                return;
              }
            }
            /* VNode object */
            if (slot.__v_isVNode) {
              slot.props = Object.assign({}, slot.props, props);
              return slot;
            }
          };

          let providerProps = {
            locale: zhCN,
            getPopupContainer: (el, dialogContext) => {
              const getDialogWrap = dialogContext && dialogContext.getDialogWrap;
              return typeof getDialogWrap === 'function' ? getDialogWrap() : getContainer();
            },
          };
          let modalProps = computed(() => {
            const footer = dialogProps.footer;
            const width = dialogProps.width || dialogSize[dialogProps.size] || dialogSize.small;
            return Object.assign({}, dialogProps, {
              ref: modalRef,
              locale: zhCN,
              width: width,
              destroyOnClose: true,
              bodyStyle: bodyStyle.value,
              maskClosable: dialogProps.maskClosable === true,
              footer: footer !== null ? undefined : null,
              getContainer: getContainer,
              onCancel: onCancel,
              onOk: onOk,
            });
          });
          let componentProps = Object.assign({}, contentProps, {
            ref: componentRef,
            onOk: (data) => tap(dialogProps.onOk, data, close),
            onCancel: (data) => tap(dialogProps.onCancel, data, close),
            onTrigger: (handler) => tap(handler, close),
            onVnodeMounted: () => {
              clearTimeout(timer);
              timer = setTimeout(() => {
                timer = null;
                ctx.emit('mounted', {
                  $modal: modalRef.value,
                  $content: componentRef.value,
                });
              }, 100);
            },
          });
          let modalSlots = {
            default: () => {
              return h(typeof content === 'function' ? defineAsyncComponent(content) : content, componentProps);
            },
          };
          let modalTitleSlot = getSlot(dialogProps.title, {
            onTrigger: (handler) => tap(handler, componentRef.value),
            onCancel: onCancel,
            onOk: onOk,
          });
          if (modalTitleSlot) {
            modalSlots.title = () => modalTitleSlot;
          }
          let modalFooterSlot = getSlot(dialogProps.footer, {
            onTrigger: (handler) => tap(handler, componentRef.value),
            onCancel: onCancel,
            onOk: onOk,
          });
          if (modalFooterSlot) {
            modalSlots.footer = () => modalFooterSlot;
          }
          onUnmounted(() => {
            styleSheet.parentNode.removeChild(styleSheet);
            styleSheet = null;
            queryHeight.dispose();
            queryHeight = null;
            dialogDiv = null;
            vNode = null;
            app = null;
            timer = null;
            content = null;
            contentProps = null;
            dialogProps = null;
          });
          return { visible, modalSlots, modalProps, providerProps };
        },
        render() {
          return h(ConfigProvider, this.providerProps, () =>
            h(Modal, Object.assign(this.modalProps, { visible: this.visible }), this.modalSlots)
          );
        },
      },
      {
        content: content,
        contentProps: contentProps,
        dialogProps: dialogProps,
        onMounted: ({ $modal, $content }) => {
          // console.log($modal, $content);
          resolve({ $modal, $content });
        },
      }
    );
    vNode.appContext = dialog.$app._context;
    app = createApp(vNode);
    app.config.globalProperties = dialog.$app.config.globalProperties;
    app._context.components = dialog.$app._context.components;
    app._context.directives = dialog.$app._context.directives;
    app._context.provides = dialog.$app._context.provides;
    app._context.mixins = dialog.$app._context.mixins;
    app._context.config = dialog.$app._context.config;
    app.mount(dialogDiv);
  });
}

export default {
  install(app) {
    defineProperty(dialog, '$app', { enumerable: true, value: app });
    defineProperty(app.config.globalProperties, '$dialog', { enumerable: true, value: dialog });
  },
};
